#ifndef _sbrush_instrument_h_
#define _sbrush_instrument_h_

#include <map>
#include <string>
#include <set>
#include <memory>
#include <iostream>

#include "sbrush_errors.h"

#include "dag.h"
#include "dag_threading.h"

namespace sbrush {
	
	class KeyError : public AcsError {
		KeyError();
	public:
		KeyError(const char* invalidKey);
		virtual ~KeyError() throw();
		const char* key() const;
	private:
		std::string m_Key;
	};
	
	class CannotHandleInstrumentState : public AcsError {
	public:
		CannotHandleInstrumentState(const std::string& reason);
		virtual ~CannotHandleInstrumentState() throw();
		const char* why() const;
		
	private:
		std::string m_Reason;
		
	};
	
	class InstrumentState {
	public:
		InstrumentState();
		InstrumentState(const InstrumentState& copy);
		virtual ~InstrumentState() throw();

		InstrumentState& operator=(const InstrumentState& rhs);		
		void set(const bool* b, size_t bSize,
				 const int* i, size_t iSize,
				 const float* f, size_t fSize,
				 const double* d, size_t dSize);
		size_t boolSize() const;
		size_t intSize() const;
		size_t floatSize() const;
		size_t doubleSize() const;
		const bool* bools() const;
		const int* ints() const;
		const float* floats() const;
		const double* doubles() const;
		bool* bools();
		int* ints();
		float* floats();
		double* doubles();
		
		int metaInt(const std::string& key) const;
		void setMetaInt(const std::string& key, int val);
		double metaDouble(const std::string& key) const;
		void setMetaDouble(const std::string& key, double val);
		const std::string& metaString(const std::string& key) const;
		void setMetaString(const std::string& key, const std::string& val);
		void resize(size_t bSize, size_t iSize, size_t fSize, size_t dSize);
		
		void serialize(std::ostream& binaryStream) const;
		static std::auto_ptr<InstrumentState> inflate(std::istream& binaryStream);
		void toFrame(dag::Frame& frame) const;
	protected:
	private:
		
		std::map<std::string, int> m_MetaInts;
		std::map<std::string, double> m_MetaDoubles;
		std::map<std::string, std::string> m_MetaStrings;
		
		size_t m_BSize;
		size_t m_ISize;
		size_t m_FSize;
		size_t m_DSize;
		bool* m_BData;
		int* m_IData;
		float* m_FData;
		double* m_DData;
	};
	
	class Instrument;
	
	class InstrumentListener {
	public:
		InstrumentListener();
		virtual ~InstrumentListener() throw();
		virtual void instrumentCallback(const Instrument* instrument,
										const InstrumentState& state) = 0;
	};
	
	class Instrument {
	public:
		Instrument();
		virtual ~Instrument() throw();
		void addListener(InstrumentListener* listener);
		void removeListener(InstrumentListener* listener);
		virtual void setState(const InstrumentState& state);
	protected:
		void broadcastState(const InstrumentState& state);
	private:
		std::set<InstrumentListener*> m_Listeners;
		std::auto_ptr<dag::Mutex> m_Mutex;
	};
}

#endif